#include "termcolor.h"
#include <QCommandLineOption>
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QDir>
#include <QException>
#include <QFile>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QJsonValue>
#include <QList>
#include <QMap>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QStringList>
#include <QTextStream>
#include <QTranslator>
#include <curses.h>
#include <iostream>

#define QENDL Qt::endl
#define PROCESSFLAG(X) ((X) != 0)

enum operationType {
  None = 0,
  Processed = 1 << 0,
  NewCode = 1 << 1, // 表示我要用模板新建文件
  AddNew = 1 << 2,
  DelNew = 1 << 3,
  ShowAll = 1 << 4,
  SetType = 1 << 5,
  CurType = 1 << 6,
  ShowInfo = 1 << 7,
  ModNew = 1 << 8,
  ClsNew = 1 << 9
};

struct codeObject {
  QString type;
  QString codePath;
  QString ext;
};

struct cmdLineParam {
  int operation = operationType::None;
  QString type;
  QString filePath;
  QString ext;
};

static QTextStream qout(stdout);
static QTextStream qin(stdin);
static QTextStream qerr(stderr);
static QRegularExpression regex("(\"[^\"]+\"|[^\\s\"]+)");

static std::string warnPrefix, errPrefix, infoPrefix;

void showWarningMsg(QString message) {
  std::cout << termcolor::yellow << warnPrefix;
  qout << message << QENDL;
}

void showErrorMsg(QString message) {
  std::cout << termcolor::red << errPrefix;
  qout << message << QENDL;
}

void showInfoMsg(QString message) {
  std::cout << termcolor::blue << infoPrefix;
  qout << message << QENDL;
}

void initLocaliztion() {
  warnPrefix = QObject::tr("WarningPrefix").toStdString();
  errPrefix = QObject::tr("ErrorPrefix").toStdString();
  infoPrefix = QObject::tr("InfoPrefix").toStdString();
}

void saveCfg(QString &path, QMap<QString, codeObject> &codeObjs) {
  QFile f(path);
  if (f.open(QFile::WriteOnly)) {
    QJsonArray objs;
    for (auto objitem : codeObjs) {
      QJsonObject obj;
      obj.insert("Type", objitem.type);
      obj.insert("CodePath", objitem.codePath);
      obj.insert("Ext", objitem.ext);
      objs.append(obj);
    }
    QJsonDocument jdoc(objs);
    if (f.write(jdoc.toJson(QJsonDocument::JsonFormat::Indented)) >= 0) {
      f.close();
    }
  }
}

QString getFilePath(QString folder, QString codePath) {
  if (codePath[0] == '/') {
    return codePath;
  } else {
    return folder + "/" + codePath;
  }
}

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);
  QCoreApplication::setApplicationVersion("1.0.0");

  auto s = a.applicationDirPath() + "/lang/default.qm";
  QTranslator translator;
  if (!translator.load(s)) {
    std::cout << termcolor::red << "[Error] Error Loading Translation File!"
              << std::endl;
    return -1;
  }
  a.installTranslator(&translator);

  initLocaliztion();

  QCoreApplication::setApplicationName(QObject::tr("NewCode"));

  QCommandLineParser parser;
  parser.setApplicationDescription(QObject::tr("AppDescription"));
  parser.addHelpOption();
  parser.addVersionOption();

  std::cout << termcolor::bright_yellow;
  qout << QObject::tr("AppDescription") << QENDL << QENDL;

  // names | description | valuename | defaultvalue
  parser.addOptions(
      {{{"t", "type"}, QObject::tr("IndicateType"), "t"},
       {{"p", "path"}, QObject::tr("OutPut"), "p"},
       {{"f", "fill", "param"}, QObject::tr("FormatParams"), "f"},
       {{"k", "keep", "alive", "keepalive"}, QObject::tr("KeepAliveFlag")},
       {{"st", "settype"}, QObject::tr("SetCurType"), "st"},
       {{"q", "quit"}, QObject::tr("Quit")},
       {"curtype", QObject::tr("ShowCurType")},
       {{"add", "a"}, QObject::tr("AddTemplate"), "add"},
       {"mod", QObject::tr("ModTemplate"), "mod"},
       {"ext", QObject::tr("IndicateExt"), "ext"},
       {"del", QObject::tr("DelTemplate"), "del"},
       {"cls", QObject::tr("ClsTemplate")},
       {"showall", QObject::tr("ShowAllTemplate")},
       {"showinfo", QObject::tr("ShowTemplate"), "showinfo"},
       {{"pwd", "curdir"}, QObject::tr("CurrentDir")},
       {"prodir", QObject::tr("ProgramDir")},
       {"cd", QObject::tr("ChangeCurDir"), "cd"}});

  parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);

  if (!parser.parse(a.arguments())) {
    showErrorMsg(parser.errorText());
  }

  QMap<QString, codeObject> codeObjs;             // 所有模板信息
  QString currentType;                            // 当前模板信息名称
  QString currentDirectory = QDir::currentPath(); // 当前路径

  // 获取模板列表
  auto jsonPath = a.applicationDirPath() + "/NewCode.json";
  QFile file(jsonPath);
  if (file.exists() && file.open(QFile::ReadOnly | QFile::Text)) {
    QJsonParseError err;
    QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &err);
    if (err.error == QJsonParseError::NoError) {
      QJsonArray cobjs = doc.array();
      for (QJsonValue item : cobjs) {
        QJsonObject obj = item.toObject();
        QJsonValue v = obj.value("Type");
        codeObject c;
        if (v == QJsonValue::Undefined)
          continue;
        c.type = v.toString();

        v = obj.value("CodePath");
        if (v == QJsonValue::Undefined)
          continue;
        c.codePath = v.toString();

        v = obj.value("Ext");
        if (v == QJsonValue::Undefined)
          continue;
        c.ext = v.toString();

        codeObjs.insert(c.type, c);
      }
    }
  }
  file.close();

  // 没有模板列表发出警告，有的话默认第一个
  if (codeObjs.count()) {
    currentType = codeObjs.firstKey();
  } else {
    showWarningMsg(QObject::tr("NoTemplate"));
  }

  bool isKeepingAlive = false;

  do {
    // 开始处理结果
    cmdLineParam param;
    QStringList fargs;

    /*======= 先记录想让我干什么，不管对错 =======*/

    // 如果命令含有退出命令，直接走人
    if (parser.isSet("q")) {
      exit(0);
    }

    // 如果命令要求帮助，直接显示重新来
    if (parser.isSet("h")) {
      qout << parser.helpText() << QENDL;
    }

    if (parser.isSet("v")) {
      qout << qApp->applicationVersion() << QENDL;
    }

    // 如果设置了默认模板类型
    if (parser.isSet("t")) {
      param.type = parser.value("t");
      param.operation |= operationType::NewCode;
    }

    // 如果设置了输出路径
    if (parser.isSet("p")) {
      param.filePath = parser.value("p");

      // 如果要执行添加模板，p 的含义发生了变化
      if (parser.isSet("add")) {
        auto i = parser.value("add");
        param.type = parser.value("add");
        param.operation |= operationType::AddNew;
      } else {
        param.operation |= operationType::NewCode;
      }
    }

    // 如果输入了格式化参数
    if (parser.isSet("f")) {
      fargs = parser.values("f");
      param.operation |= operationType::NewCode;
    }

    // 如果设置保持交互
    if (parser.isSet("k")) {
      //如果当前处于交互模式，又想让进入交互模式，发出信息告诉我已经行了
      if (isKeepingAlive) {
        showInfoMsg(QObject::tr("KeepAliveAlready"));
      }
      isKeepingAlive = true;
      param.operation |= operationType::Processed;
    }

    // 如果要执行设置类型命令
    if (parser.isSet("st")) {
      currentType = parser.value("st");
      param.operation |= operationType::SetType;
    }

    // 如果想要知道当前模板类型
    if (parser.isSet("curtype")) {
      param.operation |= operationType::CurType;
    }

    // 如果执行修改模板
    if (parser.isSet("mod")) {
      param.type = parser.value("mod");
      if (parser.isSet("ext")) {
        param.ext = parser.value("ext");
      }
      param.operation |= operationType::ModNew;
    }

    // 如果要执行删除模板操作
    if (parser.isSet("del")) {
      param.type = parser.value("del");
      param.operation |= operationType::DelNew;
    }

    // 如果要执行清空模板操作
    if (parser.isSet("cls")) {
      param.operation |= operationType::ClsNew;
    }

    // 如果想要显示所有模板信息
    if (parser.isSet("showall")) {
      param.operation |= operationType::ShowAll;
    }

    // 如果想显示某个信息
    if (parser.isSet("showinfo")) {
      param.type = parser.value("showinfo");
      param.operation |= operationType::ShowInfo;
    }

    // 如果想要显示当前目录，这个可以直接做到，且不影响其他结果
    if (parser.isSet("pwd")) {
      showInfoMsg(QObject::tr("CurrentDirectory:") + currentDirectory);
      param.operation |= operationType::Processed;
    }

    // 如果想要显示当前程序目录，这个可以直接做到，且不影响其他结果
    if (parser.isSet("prodir")) {
      showInfoMsg(QObject::tr("ProgramDirectory:") + a.applicationDirPath());
      param.operation |= operationType::Processed;
    }

    // 如果要改变当前目录，我假设你现在就需要，立即修改并做出回应
    if (parser.isSet("cd")) {
      QDir dir(currentDirectory);
      dir.cd(parser.value("cd"));
      currentDirectory = dir.absolutePath();
      showInfoMsg(currentDirectory);
      param.operation |= operationType::Processed;
    }

    /*======= 你逼逼完了，我捋一捋你想让我干什么 =======*/

    // 把你想让我干的所有可能的事情全部转为布尔
    auto flags = param.operation;
    bool hasAddNew = PROCESSFLAG(flags & operationType::AddNew);
    bool hasDelNew = PROCESSFLAG(flags & operationType::DelNew);
    bool hasCurType = PROCESSFLAG(flags & operationType::CurType);
    bool hasNewcode = PROCESSFLAG(flags & operationType::NewCode);
    bool hasShowAll = PROCESSFLAG(flags & operationType::ShowAll);
    bool hasSetType = PROCESSFLAG(flags & operationType::SetType);
    bool hasShowInfo = PROCESSFLAG(flags & operationType::ShowInfo);
    bool hasModNew = PROCESSFLAG(flags & operationType::ModNew);
    bool hasClsNew = PROCESSFLAG(flags & operationType::ClsNew);

    bool hasOp = (hasAddNew && hasDelNew) || (hasAddNew && hasModNew) ||
                 (hasAddNew && hasClsNew) || (hasDelNew && hasModNew) ||
                 (hasDelNew && hasClsNew) || (hasModNew && hasClsNew) ||
                 (hasCurType && hasSetType); // 表示有没有增删相关冲突

    bool hasOp0 = (hasShowAll && hasShowInfo) || (hasShowInfo && hasCurType) ||
                  (hasShowAll && hasCurType); // 表示有没有查询相关冲突

    // 看看有没有你既让我向东，又让我向西的操作，有的话滚
    if (hasOp || hasOp0) {
      showErrorMsg(QObject::tr("ConflitOp"));
      param.operation = operationType::None; //不知所云，不干
    }

    // 如果命令清楚，并且有使用使用模板新建文件
    if (param.operation && hasNewcode) {
      param.operation =
          operationType::NewCode; // 新建文件高于一切，除了立马下班
    }

    switch (param.operation) {
    case operationType::None:
      break;
    case operationType::NewCode: {
      // 新建文件不告诉路径，这不合理啊
      if (!param.filePath.length()) {
        showErrorMsg(QObject::tr("NoOutPutFile"));
        break;
      }

      // 没告诉我，就用当前默认模板吧
      if (!param.type.length())
        param.type = currentType;

      // 新建文件，没模板，不会，重新说一遍
      if (!param.type.length()) {
        showErrorMsg(QObject::tr("NullType"));
        break;
      }

      // 模板的名字告诉的不对啊，重来
      if (!codeObjs.contains(param.type)) {
        showErrorMsg(QObject::tr("TypeNotExists"));
        break;
      }

      // 模板找到了，继续
      auto co = codeObjs.value(currentType);

      // 尝试获取模板真正的绝对路径
      auto p = getFilePath(a.applicationDirPath(), co.codePath);

      QFile file(p);
      // 看看有没有啊
      if (!file.exists()) {
        // 小子，骗人，告诉你没有，重来
        showErrorMsg(QObject::tr(" %1 NotFound").arg(p));
        break;
      }

      // 在啊，我看看能不能读
      if (!file.open(QFile::ReadOnly | QFile::Text)) {
        // 兄弟，系统不让啊，重来
        showErrorMsg(QObject::tr(" %1 CannotRead").arg(p));
        break;
      }

      auto buffer = file.readAll(); // 出息了，读出来了
      QString fbu(buffer);
      // 如果有格式化参数就来一下
      fbu = fbu.replace("%", "%%").replace("#&", "%");
      for (auto &a : fargs) {
        fbu = QString(fbu).arg(a);
      }

      QFile fout(param.filePath);
      if (fout.exists()) {
        showWarningMsg(
            QObject::tr(" %1 ExistInputYConfirm").arg(param.filePath));

        // 有已经存在的文件，请确认一下
        char kd;
        do {
          qout << QObject::tr("YesNoChoice") << QENDL;
          kd = char(qin.readLine().toUtf8()[0] | 0x20);
        } while (kd != 'y' && kd != 'n');

        if (kd == 'n') {
          showErrorMsg(QObject::tr("CancelByUser"));
        } else {
          if (fout.open(QFile::WriteOnly)) {
            fout.write(fbu.replace("%%", "%").toUtf8());
            fout.close();
            showInfoMsg(QObject::tr("CreateSuccessfully"));
          } else {
            showErrorMsg(QObject::tr("CreateFailed"));
          }
        }
      } else {
        if (fout.open(QFile::WriteOnly)) {
          fout.write(fbu.replace("%%", "%").toUtf8());
          fout.close();
          showInfoMsg(QObject::tr("CreateSuccessfully"));
        } else {
          showErrorMsg(QObject::tr("CreateFailed"));
        }
      }
    } break;
    case operationType::AddNew: {
      if (param.type.length() &&
          QFile::exists(a.applicationDirPath() + "/" + param.filePath)) {
        if (codeObjs.contains(param.type)) {
          showErrorMsg(QObject::tr("TypeHasExists"));
        } else {
          codeObject obj;
          obj.type = param.type;
          obj.codePath = param.filePath;
          obj.ext = param.ext.length() ? param.ext : param.type;
          codeObjs.insert(param.type, obj);
          saveCfg(jsonPath, codeObjs);
          showInfoMsg(QObject::tr("InsertSuccessfully"));
        }
      } else {
        showErrorMsg(QObject::tr("InsertFailed"));
      }
    } break;
    case operationType::DelNew: {
      if (codeObjs.contains(param.type)) {
        codeObjs.remove(param.type);
        saveCfg(jsonPath, codeObjs);
        if (param.type == currentType) {
          if (codeObjs.count()) {
            currentType = codeObjs.firstKey();
          } else {
            currentType.clear();
          }
        }
        showInfoMsg(QObject::tr("DelSuccessfully"));
      } else {
        showErrorMsg(QObject::tr("DelFailed"));
      }
    } break;
    case operationType::ShowAll: {
      if (codeObjs.count() > 0) {
        qout << ">>" << QObject::tr("TypeBelow") << QENDL;
        for (auto &item : codeObjs.keys()) {
          std::cout << termcolor::green;
          qout << item << QENDL;
        }
      } else {
        qout << ">>" << QObject::tr("NoInfo") << QENDL;
      }
    } break;
    case operationType::SetType: {
      if (codeObjs.contains(param.type)) {
        currentType = param.type;
        showInfoMsg(QObject::tr("SettingSuccess"));
      } else {
        showErrorMsg(QObject::tr("TypeNotExists"));
      }
      break;
    }
    case operationType::CurType: {
      if (currentType.length()) {
        showInfoMsg(currentType);
      } else {
        showInfoMsg("NULL");
      }
    } break;
    case operationType::ShowInfo: {
      if (!codeObjs.contains(param.type)) {
        showErrorMsg(QObject::tr("KeyNotFound"));
        break;
      }
      std::cout << termcolor::yellow;
      qout << QObject::tr("InfoBelowStart") << param.type
           << QObject::tr("InfoBelowEnd") << QENDL;

      auto &item = codeObjs.value(param.type);

      std::cout << termcolor::green;
      qout << QObject::tr("CodeType") << item.type << QENDL
           << QObject::tr("CodeExt") << item.ext << QENDL
           << QObject::tr("CodePath") << item.codePath << QENDL
           << QObject::tr("CodeAbsPath")
           << getFilePath(a.applicationDirPath(), item.codePath) << QENDL;
    } break;
    case operationType::ModNew: {
      if (codeObjs.contains(param.type)) {
        auto &obj = codeObjs[param.type];
        if (param.filePath.length()) {
          obj.codePath = param.filePath;
        }
        if (param.ext.length()) {
          obj.ext = param.ext;
        }
        saveCfg(jsonPath, codeObjs);
        showInfoMsg(QObject::tr("ModSuccessfully"));
      } else {
        showErrorMsg(QObject::tr("TypeNotExists"));
      }
    } break;
    default:
      break;
    }

    if (isKeepingAlive) {
      // 等待下一次输入命令
      QString ncmd;
      do {
        std::cout << termcolor::green << ">> ";
        ncmd = qin.readLine();
        QStringList args("");

        // 开始将字符串匹配为命令行
        int off = 0;
        while (1) {
          auto match = regex.match(ncmd, off);
          if (!match.hasMatch()) {
            break;
          }
          auto res = match.captured();
          if (res[0] == '\"')
            res = res.replace("\"", "");
          if (res[0] == '\'')
            res = res.replace("'", "");
          args << res;
          off = match.capturedEnd();
        }
        if (!parser.parse(args)) {
          showErrorMsg(parser.errorText());
          ncmd.clear();
        }
      } while (!ncmd.length());
    }
  } while (isKeepingAlive);

  return 0;
}
